package com.ruoyi.common.config;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
@Slf4j
public class RedissonLockComponent {
    /**
     * 获取分布式锁最大等待时间（单位:秒）
     */
    private static final Long MAX_WAIT_TIME = 2L;

    @Resource
    private RedissonClient redissonClient;

    /**
     * 利用redis锁, 同步执行方法
     *
     * @param supplier 准备执行的方法
     * @param redisKey redis锁key
     * @param timeout  redis锁key过期时间 (单位:秒)
     * @param <T>      返回数据类型
     * @return 返回数据
     */
    public <T> T execByDistributedLock(String redisKey, Long timeout, Supplier<T> supplier) {
        return execByDistributedLock(redisKey, MAX_WAIT_TIME, timeout, supplier);
    }


    /**
     * 利用redis锁, 执行同步方法
     *
     * @param redisKey  redisKey
     * @param waitTime  最大等待分布式锁时间 (单位:秒)
     * @param leaseTime redis锁key过期时间 (单位:秒)
     * @param supplier  准备执行的方法
     * @param <T>       返回数据类型
     * @return 返回数据
     */
    public <T> T execByDistributedLock(String redisKey, long waitTime, long leaseTime, Supplier<T> supplier) {
        RLock lock = null;
        T target;
        try {
            //获取锁对象
            lock = redissonClient.getLock(redisKey);
            //加锁，并且设置锁过期时间，防止死锁的产生
            if (lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS)) {
                log.debug("redisson try lock key {} success",redisKey);
                target = supplier.get();
            } else {
                throw new RuntimeException(String.format("获取分布式锁失败,redisKey = %s", redisKey));
            }
        } catch (InterruptedException e) {
            log.error("获取分布式锁失败,请稍候再试! redisKey = {}", redisKey, e);
            throw new RuntimeException(String.format("获取分布式锁失败,redisKey = %s", redisKey));
        } finally {
            if (lock != null && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return target;
    }

}
